package com.lazy.book.service.system.impl;

import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.cache.Cache;
import cn.hutool.cache.CacheUtil;
import cn.hutool.core.bean.BeanUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.github.pagehelper.PageHelper;
import com.lazy.book.common.cache.RedisCache;
import com.lazy.book.common.config.DataScopeProperties;
import com.lazy.book.common.entity.BaseUserInfo;
import com.lazy.book.common.entity.LoginUser;
import com.lazy.book.common.entity.PageResult;
import com.lazy.book.common.enums.CommonKeyConstants;
import com.lazy.book.common.enums.CommonResponseEnum;
import com.lazy.book.common.event.EventPublisher;
import com.lazy.book.common.event.PermissionChangeEvent;
import com.lazy.book.common.event.PermissionMeta;
import com.lazy.book.common.utils.*;
import com.lazy.book.mapper.system.SysRoleMapper;
import com.lazy.book.mapper.system.SysUserRoleMapper;
import com.lazy.book.pojo.entity.SelectIdsDTO;
import com.lazy.book.pojo.system.pojo.dto.sysmenu.SysUserRoleDTO;
import com.lazy.book.pojo.system.pojo.dto.sysuser.*;
import com.lazy.book.pojo.system.pojo.po.SysRole;
import com.lazy.book.pojo.system.pojo.po.SysUserRole;
import com.lazy.book.pojo.system.pojo.vo.common.UserVO;
import com.lazy.book.pojo.system.pojo.vo.sysuser.SysUserRoleVO;
import com.lazy.book.pojo.system.pojo.vo.sysuser.UserDeptInfoVO;
import com.lazy.book.pojo.system.pojo.vo.sysuser.UserRoleInfoVO;
import com.lazy.book.service.system.*;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryChain;
import com.mybatisflex.core.query.QueryCondition;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.update.UpdateChain;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.lazy.book.mapper.system.SysUserMapper;
import com.lazy.book.pojo.system.pojo.po.SysUser;
import com.lazy.book.pojo.system.pojo.vo.sysuser.SysUserVO;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

import static com.lazy.book.pojo.system.pojo.po.table.SysUserTableDef.SYS_USER;


@Slf4j
@Service
@RequiredArgsConstructor
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {
  Cache<String, SysUser> lfuCache = CacheUtil.newLFUCache(100);

  private final SysRoleMapper sysRoleMapper;

  private final SysUserRoleMapper sysUserRoleMapper;

  private final RedisCache redisCache;

  private final SysPermissionService sysPermissionService;

  private final EventPublisher eventPublisher;

  private final SysUserDeptService userDeptService;

  private final DataScopeProperties dataScopeProperties;

  private final SysMenuService menuService;

  @Value("${spring.profiles.active}")
  private String activeProfile;

  private final AuthService authService;

  /**
   * 获取认证账户信息接角色信息
   *
   * @param username
   *            用户名
   * @return 用户信息
   */
  @Override
  public SysUserVO getSysUserByUsername(String username) {
    QueryWrapper wrapper = QueryWrapper.create().eq(SysUser::getUsername, username);

    SysUser one = getOne(wrapper);
    CommonResponseEnum.BAD_USERNAME_OR_PASSWORD.assertNull(one);
    SysUserVO sysUserVO = new SysUserVO();
    BeanCopyUtils.copy(one, sysUserVO);
    return sysUserVO;
  }

  /**
   * 获取认证账户信息接角色信息
   *
   * @param userId
   *            用户id
   * @return 用户信息
   */
  @Override
  public SysUserVO getSysUserByUserId(Long userId) {
    QueryWrapper wrapper = QueryWrapper.create().eq(SysUser::getId, userId);
    SysUser one = getOne(wrapper);
    CommonResponseEnum.BAD_USERNAME_OR_PASSWORD.assertNull(one);
    return BeanCopyUtils.copy(one, SysUserVO.class);
  }

  @Override
  public SysUser getSysUserCacheByUserId(Long userId) {
    String key = "userId" + userId;
    SysUser sysUser = lfuCache.get(key);
    if(sysUser != null){
      return sysUser;
    }
    QueryWrapper wrapper = QueryWrapper.create().eq(SysUser::getId, userId);
    SysUser one = getOne(wrapper);
    if(one != null){
      lfuCache.put(key, one);
    }
    return one;
  }

  /**
   * 后台创建用户
   *
   * @param dto
   *            用户信息
   */
  @Transactional
  @Override
  public void create(SysUserCreateDTO dto) {
    SysUser user = BeanCopyUtils.copy(dto, SysUser.class);
    QueryWrapper wrapper = QueryWrapper.create().eq(SysUser::getUsername, dto.getUsername());
    CommonResponseEnum.USERNAME_EXISTS.assertTrue(count(wrapper) > 0);
    String encodePwd = getEncoderPwd(getInitPassword());
    user.setPwd(encodePwd);
    user.setAccountStatusCd("1000001");
    user.setUserTagCd("1001003");
    save(user);

    if (dto.getDeptId() <= 0)
      return;
    UserDeptDTO deptDTO = new UserDeptDTO();
    deptDTO.setDeptIds(Collections.singletonList(dto.getDeptId()));
    deptDTO.setUserIds(Collections.singletonList(user.getId()));
    bindUserDept(deptDTO);
  }

  /**
   * 更新用户
   *
   * @param dto
   *            用户信息
   */
  @Override
  public void update(SysUserUpdateDTO dto) {
    SysUser user = BeanCopyUtils.copy(dto, SysUser.class);
    // 检查用户是否存在
    QueryWrapper wrapper = QueryWrapper.create().eq(SysUser::getId, dto.getId());
    SysUser one = getOne(wrapper);
    CommonResponseEnum.INVALID_USER.assertNull(one);
    updateById(user);
    eventPublisher.publish(new PermissionChangeEvent(this, new PermissionMeta(Collections.singletonList(user.getId()))));
  }

  /**
   * 删除用户 (逻辑删除，保留数据关系。如部门、权限、角色等)
   *
   * @param dto
   *            用户id数组
   */
  @Override
  @Transactional
  public void remove(SelectIdsDTO dto) {
    QueryWrapper wrapper = QueryWrapper.create().in(SysUser::getId, dto.getIds());
    // 检查用户是否存在
    CommonResponseEnum.INVALID_ID.assertTrue(count(wrapper) < 1);
    removeByIds(dto.getIds());
  }

  /**
   * 详情
   *
   * @param id
   *            用户id
   * @return {@link SysUser}
   */
  @Override
  public SysUserVO detail(Long id) {
    SysUser user = getById(id);
    CommonResponseEnum.INVALID_ID.assertNull(user);
    return BeanCopyUtils.copy(user, SysUserVO.class);
  }

  @Override
  public PageResult<SysUserVO> page(SysUserListDTO dto) {
    PageResult<SysUserVO> result;
    PageUtils.toPage(dto);
    try {
      List<SysUserVO> sysUserVOS;
      if (dto.getDeptId() != null && dto.getDeptId() == -1) { // 查询全部
        sysUserVOS = this.mapper.queryAllSysUserList(dto);
      } else if (dto.getDeptId() != null && dto.getDeptId() == -2) { // 查询未分配部门的列表
        sysUserVOS = this.mapper.querySysUserListNotDept(dto);
      } else { // 查询指定部门的列表
        sysUserVOS = this.mapper.querySysUserListByDept(dto);
      }
      setUserDeptInfo(sysUserVOS);
      setUserRoleInfo(sysUserVOS);
      result = PageUtils.getPageResult(sysUserVOS);
    } finally {
      PageHelper.clearPage();
    }
    return result;
  }

  private void setUserDeptInfo(List<SysUserVO> userList) {
    if (userList.isEmpty()) {
      return;
    }
    // 获取所有用户的 ID 列表
    List<Long> userIds = userList.stream().map(SysUserVO::getId).collect(Collectors.toList());

    // 查询用户的部门信息并转换为 Map
    Map<Long, UserDeptInfoVO> userDeptMap = new HashMap<>();
    List<UserDeptInfoVO> userDeptList = this.mapper.queryUserDeptInfo(userIds);
    if (userDeptList != null) {
      for (UserDeptInfoVO userDeptInfoVO : userDeptList) {
        userDeptMap.put(userDeptInfoVO.getUserId(), userDeptInfoVO);
      }
    }

    // 遍历用户列表，设置用户的部门信息
    for (SysUserVO user : userList) {
      // 检查部门信息是否存在
      if (userDeptMap.containsKey(user.getId())) {
        UserDeptInfoVO infoVO = userDeptMap.get(user.getId());
        user.setDeptInfo(infoVO.getDeptInfos());
        user.setDeptIds(infoVO.getDeptIds());
      }
    }
  }

  private void setUserRoleInfo(List<SysUserVO> userList) {
    if (userList.isEmpty()) {
      return;
    }
    // 获取所有用户的 ID 列表
    List<Long> userIds = userList.stream().map(SysUserVO::getId).collect(Collectors.toList());

    // 查询用户的部门信息并转换为 Map
    Map<Long, UserRoleInfoVO> userRoleMap = new HashMap<>();
    List<UserRoleInfoVO> userDeptList = this.mapper.queryUserRoleInfo(userIds);
    if (userDeptList != null) {
      for (UserRoleInfoVO infoVO : userDeptList) {
        userRoleMap.put(infoVO.getUserId(), infoVO);
      }
    }
    // 遍历用户列表，设置用户的部门信息
    for (SysUserVO user : userList) {
      // 检查部门信息是否存在
      if (userRoleMap.containsKey(user.getId())) {
        UserRoleInfoVO infoVO = userRoleMap.get(user.getId());
        user.setRoleInfo(infoVO.getRoleInfos());
        user.setRoleIds(infoVO.getRoleIds());
      }
    }
  }

  private String getEncoderPwd(String pwd) {
    return cn.dev33.satoken.secure.BCrypt.hashpw(pwd, cn.dev33.satoken.secure.BCrypt.gensalt(10));
  }

  private boolean matchEncoderPwd(String pwd, String pwdEncoder) {
    return cn.dev33.satoken.secure.BCrypt.checkpw(pwd, pwdEncoder);
  }

  @Override
  public SysUserRoleVO findSysUserRole(Long userId) {
    List<SysRole> sysRoleList = QueryChain.of(this.sysRoleMapper).list();
    List<SysUserRoleVO.RoleInfoVO> roleInfoVOS = BeanCopyUtils.copyList(sysRoleList, SysUserRoleVO.RoleInfoVO.class);
    List<SysUserRole> userRoles = QueryChain.of(sysUserRoleMapper).eq(SysUserRole::getUserId, userId).list();
    List<Long> roleIds = new ArrayList<>();
    if (Utils.isNotNull(userRoles)) {
      roleIds = userRoles.stream().map(SysUserRole::getRoleId).collect(Collectors.toList());
    }
    SysUserRoleVO sysUserRoleVO = new SysUserRoleVO();
    sysUserRoleVO.setRoleInfoVOS(roleInfoVOS);
    sysUserRoleVO.setSelectIds(roleIds);
    return sysUserRoleVO;
  }

  @Override
  public void changeSysUserRole(SysUserRoleDTO dto) {
    // 删除当前用户下的所有角色
    UpdateChain.of(sysUserRoleMapper).eq(SysUserRole::getUserId, dto.getUserId()).remove();

    if (Utils.isNotNull(dto.getRoleIds())) {
      sysUserRoleMapper.insertBatchSysUserRole(dto.getRoleIds(), dto.getUserId());
    }

    List<Long> userIds = new ArrayList<>();
    userIds.add(dto.getUserId());
    eventPublisher.publish(new PermissionChangeEvent(this, new PermissionMeta(userIds)));
  }

  /**
   * 获取用户信息
   *
   * @return {@link SysUserVO}
   */
  @Override
  public SysUserVO getUserInfo() {
    SysUser sysUser = getById(Objects.requireNonNull(LoginUtils.getLoginUser()).getUserInfo().getId());
    return BeanCopyUtils.copy(sysUser, SysUserVO.class);
  }

  /**
   * 更改（当前用户）密码
   *
   * @param dto
   *            dto
   */
  @Override
  public void changePassword(SysUserPasswordDTO dto) {
    SysUser sysUser = getById(StpUtil.getLoginIdAsLong()); // 获取当前用户id
    CommonResponseEnum.BAD_USERNAME_OR_PASSWORD.assertFalse(matchEncoderPwd(dto.getOldPwd(), sysUser.getPwd()));
    sysUser.setPwd(getEncoderPwd(dto.getNewPwd()));
    updateById(sysUser);
    redisCache.clearUserInfo(sysUser.getUsername());
    authService.kickOut(StpUtil.getLoginIdAsLong());
  }

  /**
   * 重置密码
   *
   * @param id
   *            id
   */
  @Override
  public void resetPassword(Long id) {
    SysUser user = getById(id);
    CommonResponseEnum.INVALID_ID.assertNull(user);
    user.setPwd(getEncoderPwd(getInitPassword()));
    updateById(user);
    authService.kickOut(id);
  }

  private String getInitPassword() {
    return SysConfigUtils.getConfValue("sys.user.initPwd");
  }

  @Override
  public void syncUserInfo(Object userId) {
    List<String> tokens = StpUtil.getTokenValueListByLoginId(userId);
    if (tokens.isEmpty()) {
      return;
    }
    // 1. 查询当前用户的最新用户权限信息
    LoginUser loginUser = buildLoginUser(Long.parseLong(userId.toString()));
    int successCount = 0;
    // todo 如果用户使用了websocket，可以结合socket来判断需要更新的“在线用户”有哪些
    for (String token : tokens) {
      try {
        // 根据token获取用户session
        SaSession saSession = StpUtil.getTokenSessionByToken(token);
        // 2. 更新redis信息
        saSession.set(LoginUtils.USER_KEY, loginUser);
        successCount++;
      } catch (SaTokenException e) {
        log.warn("token:{} 已失效, 无需同步用户信息", token);
      }
      log.info("用户元数据变更，同步更新用户信息 userId:{}, 成功:{} / {}", userId, successCount, tokens.size());
    }
  }

  @Override
  public LoginUser buildLoginUser(String username, String password) {
    boolean hasKey = RedisUtils.hasKey(CommonKeyConstants.SYS_PWD_ERR_CNT, username);
    Object value = RedisUtils.getValue(CommonKeyConstants.SYS_PWD_ERR_CNT, username);
    long count = hasKey ? Long.parseLong(String.valueOf(value)) : 0;
    if (!"preview".equals(activeProfile)) { // 预览环境不做账号锁定
      String maxErrCnt = SysConfigUtils.getConfValue("sys.pwd.errCnt");
      CommonResponseEnum.CNT_PASSWORD_ERR.assertTrue(hasKey && (count >= Utils.getIntVal(maxErrCnt)));
    }
    SysUserVO userVo = getSysUserByUsername(username);
    // 用户状态校验（禁用状态校验）
    validateUserStatus(userVo);
    // 密码校验
    validatePassword(password, userVo.getPwd(), username);
    return getLoginUser(userVo);
  }

  @Override
  public LoginUser buildLoginUser(Long userId) {
    SysUserVO userVo = getSysUserByUserId(userId);
    return getLoginUser(userVo);
  }

  @Override
  public void unlock(SelectIdsDTO dto) {
    if (dto.getIds() == null || dto.getIds().isEmpty())
      return;
    String[] ids = dto.getIds().stream().map(Utils::getStringVal).filter(Objects::nonNull).toArray(String[]::new);
    List<SysUserVO> sysUserVOS = this.mapper.queryAllSysUserNameList(ids);
    for (SysUserVO sysUserVO : sysUserVOS) {
      RedisUtils.removeKey(CommonKeyConstants.SYS_PWD_ERR_CNT, Utils.getStringVal(sysUserVO.getUsername()));
    }
  }

  private LoginUser getLoginUser(SysUserVO userVo) {
    BaseUserInfo userInfo = BeanCopyUtils.copy(userVo, BaseUserInfo.class);
    SysUser sysUser = BeanCopyUtils.copy(userVo, SysUser.class);
    CommonResponseEnum.INVALID_USER.assertNull(sysUser);
    LoginUser loginUser = new LoginUser();
    loginUser.setUserInfo(userInfo);
    loginUser.setPermissions(sysPermissionService.getMenuPermissions(sysUser)); // 获取用户permissions
    loginUser.setRoles(sysPermissionService.getRoles(sysUser)); // 获取用户角色
    loginUser.setDepts(sysPermissionService.getDepts(sysUser)); // 获取用户部门
    loginUser.setDeptAndChildren(sysPermissionService.getDeptAndChildren(sysUser)); // 获取用户部门及子孙节点
    if (!dataScopeProperties.isEnabled())
      return loginUser; // 未开启数据权限控制，结束逻辑return ！！！

    Map<String, String> btmPermissionMap = menuService.getBtnMenuByPermissions(loginUser.getPermissions());
    Set<String> findMenuIds = new HashSet<>(btmPermissionMap.values());
    loginUser.setPermissionAndMenuIds(btmPermissionMap);
    Map<String, String> ruleMap = sysPermissionService.buildMenuRuleMap(sysUser, findMenuIds);
    String customUserKey = "userRule";
    if (ruleMap.containsKey(customUserKey)) {
      String str = ruleMap.get(customUserKey);
      Map<String, Set<Long>> map = JsonUtils.parseObject(str, new TypeReference<Map<String, Set<Long>>>() {
      });
      ruleMap.remove(customUserKey);
      loginUser.setUserRuleMap(map);
    }
    String customDeptKey = "deptRule";
    if (ruleMap.containsKey(customDeptKey)) {
      String str = ruleMap.get(customDeptKey);
      Map<String, Set<Long>> map = JsonUtils.parseObject(str, new TypeReference<Map<String, Set<Long>>>() {
      });
      ruleMap.remove(customDeptKey);
      loginUser.setDeptRuleMap(map);
    }
    loginUser.setRuleMap(ruleMap); // 获取菜单的查询规则
    return loginUser;
  }

  private void validateUserStatus(SysUserVO user) {
    CommonResponseEnum.BAD_USERNAME_STATUS_INVALID.assertFalse(("1000001").equals(user.getAccountStatusCd()));
  }

  private void validatePassword(String password, String hashedPassword, String username) {
    String timeout = SysConfigUtils.getConfValue("sys_pwd.lockTime");
    boolean checkpwd = BCrypt.checkpw(password, hashedPassword);
    if (!checkpwd)
      redisCache.countPwdErr(username, Utils.getLongVal(timeout));
    CommonResponseEnum.BAD_USERNAME_OR_PASSWORD.assertFalse(checkpwd);
  }

  @Override
  public void bindUserDept(UserDeptDTO dto) {
    userDeptService.bind(dto);
    if (Utils.isNotNull(dto.getUserIds())) {
      eventPublisher.publish(new PermissionChangeEvent(this, new PermissionMeta(dto.getUserIds())));
    }
  }

  @Override
  public List<UserOptionVO> getUserOptions() {
    QueryWrapper wrapper = QueryWrapper.create().select(SYS_USER.ID, SYS_USER.USERNAME, SYS_USER.NICKNAME);
    return listAs(wrapper, UserOptionVO.class);
  }

  @Override
  public PageResult<UserVO> pageSelector(SelectorQueryDTO dto) {
    String keyword = dto.getKeyword();
    Object parentId = dto.getParentId();

    QueryWrapper wrapper = QueryWrapper.create().select(SYS_USER.ID, SYS_USER.USERNAME, SYS_USER.NICKNAME.as("name"), SYS_USER.PHONE).from(SYS_USER);

    if (parentId != null && !("-1").equals(parentId.toString())) {
      wrapper.join(SYS_USER_DEPT).on(SYS_USER.ID.eq(SYS_USER_DEPT.USER_ID)).join(SYS_DEPT_CLOSURE)
          .on(SYS_USER_DEPT.DEPT_ID.eq(SYS_DEPT_CLOSURE.DESCENDANT_ID)).join(SYS_DEPT).on(SYS_USER_DEPT.DEPT_ID.eq(SYS_DEPT.ID))
          .where(SYS_DEPT_CLOSURE.DESCENDANT_ID.isNotNull()).and(SYS_DEPT_CLOSURE.ANCESTOR_ID.eq(parentId)).groupBy(SYS_USER.ID)
          .orderBy(SYS_USER.CREATE_TIME.asc());
    }

    // 关键词条件
    if (keyword != null && !keyword.isEmpty()) {
      QueryCondition condition = SYS_USER.USERNAME.like(keyword).or(SYS_USER.NICKNAME.like(keyword)).or(SYS_USER.PHONE.like(keyword));
      wrapper.and(condition);
    }

    Page<UserVO> page = pageAs(PageUtils.getPage(dto), wrapper, UserVO.class);
    return PageUtils.getPageResult(page);
  }

}